<script setup>
import { nextTick, onMounted, ref, watchEffect } from 'vue'
import { myRequest } from '../utils/request.js'
import { aesEncrypt } from './../utils/ase.js'

// 事件触发
const props = defineProps({
  mode: { type: String, default: 'fixed' }, // 弹出模式
  captchaType: { type: String },
  vSpace: { type: Number, default: 5 },
  imgSize: {
    type: Object,
    default: () => ({ width: '310px', height: '155px' }),
  },
  barSize: {
    type: Object,
    default: () => ({ width: '310px', height: '40px' }),
  },
  defaultImg: { type: String, default: '' },
}); const emit = defineEmits(['success', 'error', 'ready'])// ✅ 代替 data() 的变量
const checkNum = 3
const fontPos = ref([])
const checkPosArr = ref([])
const num = ref(1)
const pointBackImgBase = ref('')
const poinTextList = ref([])
const backToken = ref('')
const secretKey = ref('')
const imgLeft = ref('')
const imgTop = ref('')
const showImage = ref(true)
const tempPoints = ref([])
const text = ref('')
const barAreaColor = ref('#fff')
const barAreaBorderColor = ref('#fff')
const showRefresh = ref(true)
const bindingClick = ref(true)
const canvas = ref(null) // 代替 Vue2 的 $refs.canvas

// ✅ 组件初始化
async function init() {
  fontPos.value = []
  checkPosArr.value = []
  num.value = 1
  await nextTick()
  refresh()
  emit('ready')
}

// ✅ 刷新验证码
function refresh() {
  tempPoints.value = []
  barAreaColor.value = '#000'
  barAreaBorderColor.value = '#ddd'
  bindingClick.value = true
  fontPos.value = []
  checkPosArr.value = []
  num.value = 1
  getPictrue()
  showRefresh.value = true
}

// ✅ 获取验证码图片
async function getPictrue() {
  const data = {
    captchaType: props.captchaType,
    clientUid: uni.getStorageSync('point'),
    ts: Date.now(),
  }
  try {
    const result = await myRequest({
      url: '/admin-api/system/captcha/get',
      data,
      method: 'POST',
    })
    const res = result.data
    if (res.repCode === '0000') {
      pointBackImgBase.value = res.repData.originalImageBase64
      backToken.value = res.repData.token
      secretKey.value = res.repData.secretKey
      poinTextList.value = res.repData.wordList
      text.value = `请依次点击【${poinTextList.value.join(',')}】`
    }
    if (res.repCode === '6201') {
      pointBackImgBase.value = null
    }
  }
  catch (error) {
    console.error('获取验证码图片失败', error)
  }
}

// ✅ 计算点击位置
function getMousePos(e) {
  return {
    x: Math.ceil(e.detail.x) - imgLeft.value,
    y: Math.ceil(e.detail.y) - imgTop.value,
  }
}

// ✅ 处理点击验证码
function canvasClick(e) {
  const query = uni.createSelectorQuery().in(this)
  query.select('#image').boundingClientRect((data) => {
    imgLeft.value = Math.ceil(data.left)
    imgTop.value = Math.ceil(data.top)

    checkPosArr.value.push(getMousePos(e))
    if (num.value === checkNum) {
      num.value = createPoint(getMousePos(e))

      // 按比例转换坐标
      checkPosArr.value = pointTransfrom(checkPosArr.value, props.imgSize)

      setTimeout(async () => {
        const captchaVerification = secretKey.value
          ? aesEncrypt(
              `${backToken.value}---${JSON.stringify(checkPosArr.value)}`,
              secretKey.value,
            )
          : `${backToken.value}---${JSON.stringify(checkPosArr.value)}`

        const data = {
          captchaType: props.captchaType,
          pointJson: secretKey.value
            ? aesEncrypt(JSON.stringify(checkPosArr.value), secretKey.value)
            : JSON.stringify(checkPosArr.value),
          token: backToken.value,
        }

        try {
          const result = await myRequest({
            url: '/admin-api/system/captcha/check',
            data,
            method: 'POST',
          })
          const res = result.data
          if (res.repCode === '0000') {
            barAreaColor.value = '#4cae4c'
            barAreaBorderColor.value = '#5cb85c'
            text.value = '验证成功'
            bindingClick.value = false
            setTimeout(() => {
              if (props.mode === 'pop') {
                emit('close') // 让父组件控制弹窗关闭
              }
              refresh()
            }, 1500)
            emit('success', { captchaVerification })
          }
          else {
            emit('error')
            barAreaColor.value = '#d9534f'
            barAreaBorderColor.value = '#d9534f'
            text.value = '验证失败'
            setTimeout(refresh, 700)
          }
        }
        catch (error) {
          console.error('验证码校验失败', error)
        }
      }, 400)
    }

    if (num.value < checkNum) {
      num.value = createPoint(getMousePos(e))
    }
  }).exec()
}

// ✅ 创建点击点
function createPoint(pos) {
  tempPoints.value.push({ ...pos })
  return ++num.value
}

// ✅ 坐标转换
function pointTransfrom(pointArr, imgSize) {
  return pointArr.map(p => ({
    x: Math.round((310 * p.x) / Number.parseInt(imgSize.width)),
    y: Math.round((155 * p.y) / Number.parseInt(imgSize.height)),
  }))
}

// ✅ 监听 captchaType 变化，自动刷新
watchEffect(() => {
  if (props.captchaType) {
    init()
  }
})

// ✅ 组件挂载后初始化
onMounted(() => {
  console.log('默认图片:', props.defaultImg)
  init()
})
</script>

<template>
  <view style="position: relative">
    <view v-show="showImage" class="verify-image-out">
      <view class="verify-image-panel" :style="{ width: imgSize.width, height: imgSize.height, marginBottom: `${vSpace}px` }">
        <view v-show="showRefresh" class="verify-refresh" style="z-index:3" @click="refresh">
          <text class="iconfont icon-refresh" />
        </view>
        <image
          id="image"
          ref="canvas" :src="pointBackImgBase ? `data:image/png;base64,${pointBackImgBase}` : defaultImg" style="width:100%;height:100%;display:block"
          @click="bindingClick ? canvasClick($event) : undefined"
        />
      </view>
    </view>
    <view class="verify-bar-area" :style="{ width: imgSize.width, color: barAreaColor, borderColor: barAreaBorderColor, lineHeight: '40px' }">
      <text class="verify-msg">{{ text }}</text>
    </view>
  </view>
</template>

<style scoped>
	.verifybox {
  position: relative;
  box-sizing: border-box;
  border-radius: 2px;
  border: 1px solid #e4e7eb;
  background-color: #fff;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.verifybox-top {
  padding: 0 15px;
  height: 50px;
  line-height: 50px;
  text-align: left;
  font-size: 16px;
  color: #45494c;
  border-bottom: 1px solid #e4e7eb;
  box-sizing: border-box;
}

.verifybox-bottom {
  /* padding: 15px; */
  box-sizing: border-box;
}

.verifybox-close {
  position: absolute;
  top: 13px;
  right: 9px;
  width: 24px;
  height: 24px;
  text-align: center;
  cursor: pointer;
}

.mask {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1001;
  width: 100%;
  height: 100vh;
  background: rgba(0, 0, 0, 0.3);
  /* display: none; */
  transition: all 0.5s;
}

.verify-tips {
  position: absolute;
  left: 0px;
  bottom: 0px;
  width: 100%;
  height: 30px;
  background-color: rgb(231, 27, 27, 0.5);
  line-height: 30px;
  color: #fff;
}

.tips-enter,
.tips-leave-to {
  bottom: -30px;
}

.tips-enter-active,
.tips-leave-active {
  transition: bottom 0.5s;
}

/* ---------------------------- */
/*常规验证码*/
.verify-code {
  font-size: 20px;
  text-align: center;
  cursor: pointer;
  margin-bottom: 5px;
  border: 1px solid #ddd;
}

.cerify-code-panel {
  height: 100%;
  overflow: hidden;
}

.verify-code-area {
  float: left;
}

.verify-input-area {
  float: left;
  width: 60%;
  padding-right: 10px;
}

.verify-change-area {
  line-height: 30px;
  float: left;
}

.varify-input-code {
  display: inline-block;
  width: 100%;
  height: 25px;
}

.verify-change-code {
  color: #337ab7;
  cursor: pointer;
}

.verify-btn {
  width: 200px;
  height: 30px;
  background-color: #337ab7;
  color: #ffffff;
  border: none;
  margin-top: 10px;
}

/*滑动验证码*/
.verify-bar-area {
  position: relative;
  background: #ffffff;
  text-align: center;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  border: 1px solid #ddd;
  -webkit-border-radius: 4px;
}

.verify-bar-area .verify-move-block {
  position: absolute;
  top: 0px;
  left: 0;
  background: #fff;
  cursor: pointer;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  box-shadow: 0 0 2px #888888;
  -webkit-border-radius: 1px;
}

.verify-bar-area .verify-move-block:hover {
  background-color: #337ab7;
  color: #ffffff;
}

.verify-bar-area .verify-left-bar {
  position: absolute;
  top: -1px;
  left: -1px;
  background: #f0fff0;
  cursor: pointer;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  border: 1px solid #ddd;
}

.verify-image-panel {
  margin: 0;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  border-top: 1px solid #ddd;
  border-bottom: 1px solid #ddd;
  border-radius: 3px;
  position: relative;
}

.verify-image-panel .verify-refresh {
  width: 25px;
  height: 25px;
  text-align: center;
  padding: 5px;
  cursor: pointer;
  position: absolute;
  top: 0;
  right: 0;
  z-index: 2;
}

.verify-image-panel .icon-refresh {
  font-size: 20px;
  color: #fff;
}

.verify-image-panel .verify-gap {
  background-color: #fff;
  position: relative;
  z-index: 2;
  border: 1px solid #fff;
}

.verify-bar-area .verify-move-block .verify-sub-block {
  position: absolute;
  text-align: center;
  z-index: 3;
  /* border: 1px solid #fff; */
}

.verify-bar-area .verify-move-block .verify-icon {
  font-size: 18px;
}

.verify-bar-area .verify-msg {
  z-index: 3;
}

/*字体图标的css*/
/*@font-face {font-family: "iconfont";*/
/*src: url('../fonts/iconfont.eot?t=1508229193188'); !* IE9*!*/
/*src: url('../fonts/iconfont.eot?t=1508229193188#iefix') format('embedded-opentype'), !* IE6-IE8 *!*/
/*url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),*/
/*url('../fonts/iconfont.ttf?t=1508229193188') format('truetype'), !* chrome, firefox, opera, Safari, Android, iOS 4.2+*!*/
/*url('../fonts/iconfont.svg?t=1508229193188#iconfont') format('svg'); !* iOS 4.1- *!*/
/*}*/

.iconfont {
  font-family: 'iconfont' !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-check:before {
  content: ' ';
  display: block;
  width: 16px;
  height: 16px;
  position: absolute;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 9999;
  background-image: url('');
  background-size: contain;
}

.icon-close:before {
  content: ' ';
  display: block;
  width: 16px;
  height: 16px;
  position: absolute;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 9999;
  background-image: url('');
  background-size: contain;
}

.icon-right:before {
  content: ' ';
  display: block;
  width: 16px;
  height: 16px;
  position: absolute;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background-size: cover;
  z-index: 9999;
  background-image: url('');
  background-size: contain;
}

.icon-refresh:before {
  content: ' ';
  display: block;
  width: 16px;
  height: 16px;
  position: absolute;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 9999;
  background-image: url('');
  background-size: contain;
}
</style>
